`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date:    22:41:36 12/05/2008 
// Design Name: 
// Module Name:    VGARead 
// Project Name: 
// Target Devices: 
// Tool versions: 
// Description: 
//	VGARead is a state machine which helps VGA controller to gather data from block ram.
//	It has 8 states. The first four states read the index of tiles of the whole row
//	The second four states read the actual pixel from block ram and store into frameBuffer
// after compare.
// Dependencies: 
//
// Revision: 
// Revision 0.01 - File Created
// Additional Comments: 
//
//////////////////////////////////////////////////////////////////////////////////
module VGARead(
	input clock,
	input reset,
	input [15:0] frontTileIndex,
	input [15:0] backTileIndex,
	input [15:0] frontPixelData,
	input [15:0] backPixelData,
	input [4:0] currentVerticalPixelCount,
	input [9:0] verticalTileIndexOffset,
	input startToRead,
	output reg frontPixelDataEnable,
	output reg backPixelDataEnable,
	output reg writeBackTileEnable,
	output reg writeFrontTileEnable,
	output reg writePixelDataEnable,
	output reg [4:0] tileIndex = 0,
	output reg [7:0] hPixelCount = 0,
	output reg [4:0] readTileIndex = 0,
	output reg [15:0] address = 0,
	output reg en = 1,
	output reg [15:0] pixelData = 0
	 );
	 
parameter TILE_MEMORY_ADDRESS = 16'h0B08;
parameter BACK_FRAME_MEMORY_ADDRESS = 16'h0400;
parameter FRAME_MEMORY_ADDRESS = 16'h0784;


parameter resetState = 3'b000;
parameter writeFrontTileIndex = 3'b001;
parameter writeBackTileIndex = 3'b010;
parameter readFrontTileIndex = 3'b011;
parameter resetPixelState = 3'b100;
parameter writeFrontTilePixel = 3'b101;
parameter writeBackTilePixel = 3'b110;
parameter readFrontTilePixel = 3'b111;

reg offset2 = 0;
reg offset = 0;
reg [2:0] CS = 0;
reg [2:0] NS = 0;
reg [4:0] testTileIndex = 0;
reg [2:0] currentHorizontalPixelCount = 3'd1;
//Reading data state machine
always@(posedge clock)
begin
	if(reset) CS <= resetState;
	else CS <= NS;
end
//Next State Logic
always@(CS, startToRead, testTileIndex, hPixelCount)
begin
	case(CS)
		resetState:					
		begin
			if(startToRead == 1)
				NS <= writeFrontTileIndex;
			else
				NS <= resetState;
		end
		writeFrontTileIndex:		
		begin
			NS <= writeBackTileIndex;
		end
		writeBackTileIndex:
			begin
				if(testTileIndex == 5'd29)
				NS <= resetPixelState;
				else
				NS <= readFrontTileIndex;
			end
		readFrontTileIndex:		NS <= writeFrontTileIndex;
		
		
		resetPixelState:
		begin
			NS <= writeFrontTilePixel;
		end
		writeFrontTilePixel:	   NS <= writeBackTilePixel;
		writeBackTilePixel:		NS <= readFrontTilePixel;
		readFrontTilePixel:
			begin
				if(hPixelCount == 8'd239)
					NS <= resetState;
				else
					NS <= writeFrontTilePixel;
			end
		default:	NS <= resetState;

	endcase
end
//output logic

always@(posedge clock)
begin
	writeBackTileEnable <= 0;
	writeFrontTileEnable <= 0;
	writePixelDataEnable <= 0;
	frontPixelDataEnable <= 0;
	backPixelDataEnable <= 0;
	en <= 1;
	case(CS)
		resetState:
			begin
				address <= FRAME_MEMORY_ADDRESS + testTileIndex + verticalTileIndexOffset;
				tileIndex <= 5'd0;
				testTileIndex <= 5'd0;
				hPixelCount <= 8'd0;
				currentHorizontalPixelCount <= 4'd1;
				readTileIndex <= 5'd0;
				offset <= 0;
				end
		writeFrontTileIndex:
			begin
				writeFrontTileEnable <= 1;
				address <= BACK_FRAME_MEMORY_ADDRESS + testTileIndex + verticalTileIndexOffset;
			end
		writeBackTileIndex:
			begin
				writeBackTileEnable <= 1;
				testTileIndex <= testTileIndex + 1;
					if(testTileIndex == 5'd30)
						testTileIndex <= 5'd0;
			end
		readFrontTileIndex:
			begin
				address <= FRAME_MEMORY_ADDRESS + testTileIndex + verticalTileIndexOffset;
				tileIndex <= tileIndex + 1;
					if(tileIndex == 5'd30)
						tileIndex <= 5'd0;
				end
		resetPixelState:
			begin
				address <= TILE_MEMORY_ADDRESS +  (frontTileIndex << 7) + (currentVerticalPixelCount << 3) + offset;
				readTileIndex <= 5'd0;
				pixelData <= 16'b0;
			end
		writeFrontTilePixel:
			begin
				frontPixelDataEnable <= 1'b1;
				address <= TILE_MEMORY_ADDRESS + (backTileIndex << 7)+ currentHorizontalPixelCount + (currentVerticalPixelCount<< 3) + offset;
			end
		writeBackTilePixel:
			begin
				backPixelDataEnable <= 1'b1;	
			if(currentHorizontalPixelCount == 3'd7)
				begin
					readTileIndex <= readTileIndex + 1'b1;
				end
			if(currentVerticalPixelCount == 5'd15)
				offset <= 0;
			else if(readTileIndex ==29)
				offset <= 0;
			else
				offset <= 1;
			if(offset2 == 1'b1)
				offset2 <= 0;
			end
		readFrontTilePixel:
			begin
				if(readTileIndex == 30)
					readTileIndex <= 5'd0;
				currentHorizontalPixelCount <= currentHorizontalPixelCount + 1;
				writePixelDataEnable <= 1;
				hPixelCount <= hPixelCount + 1;
				if(frontPixelData[8] == 1)
					pixelData[15:8] <= frontPixelData[15:8];
				else
					pixelData[15:8] <= backPixelData[15:8];
					
				if(frontPixelData[0] == 1)
					pixelData[7:0] <= frontPixelData[7:0];
				else
					pixelData[7:0] <= backPixelData[7:0];
				address <= TILE_MEMORY_ADDRESS +  (frontTileIndex << 7) + currentHorizontalPixelCount + (currentVerticalPixelCount << 3) + offset;
			end
		default:
			begin
				writeBackTileEnable <= 0;
				writeFrontTileEnable <= 0;
				writePixelDataEnable <= 0;
				pixelData <= 16'b0;
				readTileIndex <= 5'd0;
				address <= FRAME_MEMORY_ADDRESS;
				tileIndex <= 5'd0;
				hPixelCount <= 8'd0;
				currentHorizontalPixelCount <= 3'd0;
				readTileIndex <= 5'd0;
			end
	endcase
end
endmodule
